<?php
/**
 * Created by PhpStorm.
 * User: martindyrby
 * Date: Oct 27, 2010
 * Time: 4:02:35 PM
 * To change this template use File | Settings | File Templates.
 */
 
class ActoParser {

    const VIEW_TYPE = "actoViewType";
    const DEPENDS = "actoDepends";
    const EXTEND = "actoExtends";
    const TYPE = "actoType";
    const PROPS = "actoProps";

    const PROP_KEYS = "actoProps_key";
    const PROP_VALUES = "actoProps_value";

    const BLOCK_TYPE = "block";
    const PAGE_TYPE = "page";
    const NONE = "none";

    const EXTENDS_SETTINGS = "actoExtends";
    const TYPES_SETTINGS = "actoTypes";
    const UPDATE_SETTINGS = "update_actoParserSettings";

    const INSET = "-";
    const DELIMITER = ":";

    var $adminOptionsName = "ActoParserAdminOptions";

       var $actoHeader = '<?xml version="1.0" encoding="UTF-8" ?>
<acto>
    ';
        var $actoFooter = '
</acto>';

    
    function init() {
        global $wp_rewrite;

        $this->getAdminOptions();
        
        add_filter('rewrite_rules_array', array(&$this, 'rewrites'));

 		add_filter('query_vars', array(&$this, 'query_vars'));

		add_action('template_redirect', array(&$this, 'template_redirect'));

        $wp_rewrite->flush_rules();

        require_once(ABSPATH . 'wp-admin/includes/template.php');
        add_meta_box("acto_meta_data", "Acto META DATA", array(&$this, 'buildActoMetaDataPane'), "page", "normal", "core");
        add_meta_box("acto_prop_manager", "Acto Prop Manager", array(&$this, 'buildActoPropManagerPane'), "page", "normal", "core");
        remove_meta_box('postcustom', 'page', 'normal');

        add_action( 'save_post', array(&$this, 'saveMetaData'), 100, 2 );

        add_action('admin_menu', array(&$this, 'on_admin_menu'));

    }



    function on_admin_menu() {

      add_options_page('Acto Parser Settings', 'Acto Parser plugin', 'manage_options', basename(__FILE__), array(&$this, 'printAdminPage'));

    }

    //Returns an array of admin options
    function getAdminOptions() {
        $actoAdminOptions = array('extends' => '',
            'types' => '');
        $actoOptions = get_option($this->adminOptionsName);
        if (!empty($actoOptions)) {
            foreach ($actoOptions as $key => $option)
                $actoAdminOptions[$key] = $option;
        }
        update_option($this->adminOptionsName, $actoAdminOptions);
        return $actoAdminOptions;
    }

    //Prints out the admin page
	function printAdminPage() {
        $actoOptions = $this->getAdminOptions();
        if (isset($_POST['update_actoParserSettings'])) {
            if (isset($_POST[self::EXTENDS_SETTINGS])) {
                $actoOptions[self::EXTENDS_SETTINGS] = $_POST[self::EXTENDS_SETTINGS];
            }
            if (isset($_POST[self::TYPES_SETTINGS])) {
                $actoOptions[self::TYPES_SETTINGS] = $_POST[self::TYPES_SETTINGS];
            }
            update_option($this->adminOptionsName, $actoOptions);

            echo '<div class="updated"><p><strong>Acto Settings Updated.</strong></p></div>';
        }


        echo'
            <div class="wrap">
                <form method="post" action="'.$_SERVER["REQUEST_URI"].'">
                    <h2>ACTO Parser </h2>

                    <h3>Possible values for <u>extends</u><h3>
                    <p>Comma-separated</p>
                    <p><label for="'.self::EXTENDS_SETTINGS.'"><input type="text" name="'.self::EXTENDS_SETTINGS.'" value="'.$actoOptions[self::EXTENDS_SETTINGS].'" /></p>

                    <h3>Possible values for <u>types</u><h3>
                    <p>Comma-separated</p>
                    <p><label for="'.self::TYPES_SETTINGS.'"><input type="text" name="'.self::TYPES_SETTINGS.'" value="'.$actoOptions[self::TYPES_SETTINGS].'" /></p>

                    <div class="submit">
                        <input type="submit" name="'.self::UPDATE_SETTINGS.'" value="Update Settings" />
                    </div>
                </form>
             </div>';
	}

    function buildExistingProps($props, $layoutPrefix) {
        $output = "";

        foreach($props as $prop) {
            $output .= $layoutPrefix.$prop['key'].":".$prop['value'];
            $output .= $this->buildExistingProps($prop['nested'], $layoutPrefix.'-');
        }

        return $output;
    }

    function buildActoPropManagerPane($post) {

        $props = get_post_meta($post->ID, self::PROPS, true);

        $deserialized = unserialize($props);


        ?>
        <table width="100%">
            <tr>

                <td width="60%">
        <textarea name="<?= self::PROPS ?>" style="width:100%;" rows="30"><?= $this->buildExistingProps($deserialized,'') ?></textarea>
                </td>
                <td width="40%" style="vertical-align:top;">
                    <h3>Syntax</h3>
                    <p>
                    key1:value1<br />
                    -subkey1:subvalue1<br />
                    key2:value2<br />
                    </p>

                    <h3>Result</h3>

                    &lt;prop name="key1" value="value1"&gt;<br />
                    &nbsp;&nbsp;&lt;prop name="subkey1" value="subvalue1" /&gt;<br />
                    &lt;/prop&gt;<br />
                    &lt;prop name="key2" value="value2" /&gt;<br />

                </td>
            </tr>

        </table>

        <?php
    }


    function buildActoMetaDataPane($post) {

        echo '<table';

        $viewType = get_post_meta($post->ID, self::VIEW_TYPE, true);

        // PAGE/BLOCK switch

        $this->addMetaDataPaneInput('<input type="radio" name="'.self::VIEW_TYPE.'" value="page" '.(($viewType != 'block') ? 'checked="checked"':'').'/>', 'Page','<input type="radio" name="'.self::VIEW_TYPE.'" value="block" '.(($viewType == 'block') ? 'checked="checked"':'').'/>', 'Block');









        // BLOCK depends
        $pages = get_pages();
        $curDependsMeta = get_post_meta($post->ID, self::DEPENDS, true);
        $curDepends = explode(',', $curDependsMeta);
        $dependencySelector = '<select id="'.self::DEPENDS.'" name="'.self::DEPENDS.'[]" multiple="multiple" size="4" style="height:8em">';

        foreach($pages as $page) {

            $viewType = get_post_meta($page->ID, self::VIEW_TYPE, true);

            if($viewType == self::BLOCK_TYPE) {
                $selected = '';
                foreach($curDepends as $curDependency) {
                    if($page->post_name == $curDependency) {
                        $selected = ' selected="selected"';

                        break;
                    }
                }
                $dependencySelector .= '<option value="'.$page->post_name.'"'.$selected.'>'.$page->post_title.'</option>';
            }
        }

        $dependencySelector .= '</select>';

        $this->addMetaDataPaneInput('<label>Choose dependencies</label>', $dependencySelector, '<button onclick="javascript:jQuery(\'#'.self::DEPENDS.' option\').attr(\'selected\', false);return false;">Clear</button>', '');





        // Extends

        $actoOptions = $this->getAdminOptions();
        $curExtends = get_post_meta($post->ID, self::EXTEND, true);
        $extends = explode(',', $actoOptions[self::EXTENDS_SETTINGS]);

        $extendsSelector = '<select name="'.self::EXTEND.'" size="1"><option value="'.self::NONE.'"> - </option>';

        foreach($extends as $extendable) {

            $selected = '';
            if($extendable == $curExtends) {
                $selected = ' selected="selected"';
            }
            $extendsSelector .= '<option value="'.$extendable.'"'.$selected.'>'.$extendable.'</option>';
        }

        $extendsSelector .= '</select>';

        $this->addMetaDataPaneInput('<label>Choose extends</label>', $extendsSelector, '', '');






        // Types

        $actoOptions = $this->getAdminOptions();
        $curType = get_post_meta($post->ID, self::TYPE, true);
        $type = explode(',', $actoOptions[self::TYPES_SETTINGS]);

        $typeSelector = '<select name="'.self::TYPE.'" size="1"><option value="'.self::NONE.'"> - </option>';

        foreach($type as $typeable) {

            $selected = '';
            if($typeable == $curType) {
                $selected = ' selected="selected"';
            }
            $typeSelector .= '<option value="'.$typeable.'"'.$selected.'>'.$typeable.'</option>';
        }

        $typeSelector .= '</select>';

        $this->addMetaDataPaneInput('<label>Choose Type</label>', $typeSelector, '', '');




        echo '</table>';
    }














    function addMetaDataPaneInput($td1, $td2, $td3, $td4) {


        echo '<tr style="vertical-align:top"><td style="vertical-align:top"><table><tr>';

        if($td1 != '') echo '<td style="vertical-align:top">'.$td1.'</td>';
        if($td2 != '') echo '<td style="vertical-align:top">'.$td2.'</td>';
        if($td3 != '') echo '<td style="vertical-align:top">'.$td3.'</td>';
        if($td4 != '') echo '<td style="vertical-align:top">'.$td4.'</td>';

        echo '</tr></table></td></tr>';
    }



    function rewrites($wp_rules) {
        // Register the rewrite rule /acto/[method] => ?acto=[method]
        $acto_api_rules = array(
            'acto/(.+)$' => 'index.php?actoo=$matches[1]',
            'acto$' => 'index.php?actoo=getCompleteParse',
            'acto/$' => 'index.php?actoo=getCompleteParse'
        );

        $merged = array_merge($acto_api_rules, $wp_rules);
        
        return $merged;
    }

	function query_vars($wp_vars) {
		// This makes our variables available from the get_query_var WP function
        array_push($wp_vars, 'actoo');
        return $wp_vars;
	}

	function template_redirect() {
		if(get_query_var('actoo') != "") {

			do_action("acto_parser_$method");


            $result = $this->getCompleteParse();

            header('Content-Type: text/xml');

            echo $this->actoHeader.$result.$this->actoFooter;

			exit;
		}
	}


    function buildPropsArray($postFix) {

        $output = array();
        $propLines = explode("
", $_REQUEST[self::PROPS]);
        $inset = "";
        $curOutput = &$output;

        $outputs = array($inset => &$output);



        foreach($propLines as $propLine) {

            $tmpinset = "";

            for($i = 0; $i < strlen($propLine); $i++) {
                if(substr($propLine, $i, 1) == self::INSET) {
                    $tmpinset .= self::INSET;
                }
            }

            $entry = substr($propLine, strlen($tmpinset));
            $entryParts = explode(self::DELIMITER, $entry);


            if(strlen($tmpinset) > strlen($inset)) { // sub level
                $outputs[$tmpinset] = &$outputs[$inset][count($outputs[$inset]) - 1]['nested'];
            }

            $inset = $tmpinset;

            $index = count($outputs[$inset]);
            if($this->is_key($entryParts[0])) {
                $outputs[$inset][$index]['key'] = $entryParts[0];
                if(count($entryParts) > 1) {
                    $outputs[$inset][$index]['value'] = $entryParts[1];
                }
                $outputs[$inset][$index]['nested'] = array();
            }
        }


        return $output;
    }



    function saveMetaData( $id, $post ) {

        // SAVING VIEW TYPE PAGE/BLOCK
        $viewType = $_REQUEST[self::VIEW_TYPE];

        if ( !add_post_meta( $id, self::VIEW_TYPE, $viewType, true ) ) {
            update_post_meta( $id, self::VIEW_TYPE, $viewType );
        }


        // SAVING DEPENDS
        $dependsRequests = $_REQUEST[self::DEPENDS];
        if(is_array($dependsRequests)) {
            for($i = count($dependsRequests); $i >=0; $i--) {
                if($dependsRequests[$i] == self::NONE) {
                    $dependsRequests = array();
                    break;
                }
            }
            $depends = implode($dependsRequests, ',');
        } else {
            $depends = ($dependsRequests != self::NONE) ? $dependsRequests : "";
        }

        if($depends == "") {
            delete_post_meta( $id, self::DEPENDS);
        } else if ( !add_post_meta( $id, self::DEPENDS, $depends, true ) ) {
            update_post_meta( $id, self::DEPENDS, $depends );
        }



        // SAVING TYPE
        $type = $_REQUEST[self::TYPE];
        if($type == self::NONE) {
            delete_post_meta( $id, self::TYPE);
        } else if ( !add_post_meta( $id, self::TYPE, $type, true ) ) {
            update_post_meta( $id, self::TYPE, $type );
        }



        // SAVING EXTEND
        $extend = $_REQUEST[self::EXTEND];
        if($extend == self::NONE) {
            delete_post_meta( $id, self::EXTEND);
        } else if ( !add_post_meta( $id, self::EXTEND, $extend, true ) ) {
            update_post_meta( $id, self::EXTEND, $extend );
        }




        // SAVING PROPS

        $propArray = $this->buildPropsArray("");

        $props = serialize($propArray);

        if ( !add_post_meta( $id, self::PROPS, $props, true ) ) {
            update_post_meta( $id, self::PROPS, $props );
        }

    }




    




    public function getCompleteParse() {


        // PAGES / BLOCKS
        $pages = get_pages();
        $pagesTags = "
    <views>";


        foreach($pages as $page) {
            $id = ' id="'.$page->post_name.'"';

            $viewTypeMeta = get_post_meta($page->ID, self::VIEW_TYPE, true);
            $viewType = ($viewTypeMeta != '') ? $viewTypeMeta : 'page';


            $dependsMeta = get_post_meta($page->ID, self::DEPENDS, true);
            $depends = ($dependsMeta != "") ? ' depends="'.$dependsMeta.'"' : '';

            $extendMeta = get_post_meta($page->ID, self::EXTEND, true);
            $extend = ($extendMeta != "") ? ' extends="'.$extendMeta.'"' : '';
            $typeMeta = get_post_meta($page->ID, self::TYPE, true);
            $type = ($typeMeta != "") ? ' type="'.$typeMeta.'"' : '';

            // PROPS
            $props = get_post_meta($page->ID, self::PROPS, true);

            $deserializedProps = unserialize($props);


            $pagesTags .= '
        <'.$viewType.$id.$depends.$type.$extend.'>
            <prop name="title"><![CDATA['.$page->post_title.']]></prop>
            <prop name="content"><![CDATA['.$page->post_content.']]></prop>
            <prop name="excerpt"><![CDATA['.$page->post_excerpt.']]></prop>
            '.$this->buildProps($deserializedProps).'            
        </'.$viewType.'>';
        }

        $pagesTags .= "
    </views>";







        // POSTS
        $categories = get_categories();
        $feed = '
    <prop name="feeds">';
        foreach($categories as $category) {
            $posts = get_posts("category=".$category->cat_ID);
            $feed .= '
        <prop name="'.$category->category_nicename.'">';
            foreach($posts as $post) {
                $feed .= '
            <prop name="'.$post->post_name.'">
                <prop name="title"><![CDATA['.$post->post_title.']]></prop>
                <prop name="author"><![CDATA['.$post->post_author.']]></prop>
                <prop name="date"><![CDATA['.$post->post_date.']]></prop>
                <prop name="excerpt"><![CDATA['.$post->post_excerpt.']]></prop>
                <prop name="status"><![CDATA['.$post->post_status.']]></prop>
                <prop name="content"><![CDATA['.$post->post_content.']]></prop>
            </prop>';
            }

            $feed .= '
        </prop>';
        }
        $feed .= '
    </prop>';




        return $pagesTags.$feed;
        
    }

    function buildProps($props) {
        $output = '';
        if(is_array($props)) {
            foreach($props as $prop) {

                $key = ' name="'.$prop['key'].'"';
                $value = isset($prop['value']) ? ' value="'.$prop['value'].'"' : '';

                $output .= '<prop'.$key.$value.'>';

                $output .= isset($prop['nested']) ? $this->buildProps($prop['nested']) : '';

                $output .= '</prop>';
            }
            
        }

        return $output;
    }

    function is_key($key) {

        return trim($key) != "";
    }
}





/*
stdClass Category
(
    [term_id] => 14
    [name] => News
    [slug] => news
    [term_group] => 0
    [term_taxonomy_id] => 14
    [taxonomy] => category
    [description] =>
    [parent] => 0
    [count] => 1
    [cat_ID] => 14
    [category_count] => 1
    [category_description] =>
    [cat_name] => News
    [category_nicename] => news
    [category_parent] => 0
)

 */
/*
stdClass Post
(
    [ID] => 37
    [post_author] => 1
    [post_date] => 2010-09-30 13:05:58
    [post_date_gmt] => 2010-09-30 11:05:58
    [post_content] => So now the FOTB2010 is over and I got to do my 3-minute elevator pitch on using MIDI controllers to tweak values in flash movies in runtime.
The video of the speak along with the other elevator pitches will be available at the FOTB website, I think.
To download the classes and technology that I used at the pitch, go to http://www.molalabs.com
    [post_title] => 3-minute elevator pitch @ FOTB2010
    [post_category] => 0
    [post_excerpt] =>
    [post_status] => publish
    [comment_status] => open
    [ping_status] => open
    [post_password] =>
    [post_name] => 3-minute-elevator-pitch-fotb2010
    [to_ping] =>
    [pinged] =>
    [post_modified] => 2010-09-30 13:05:58
    [post_modified_gmt] => 2010-09-30 11:05:58
    [post_content_filtered] =>
    [post_parent] => 0
    [guid] => http://blog.flashbordon.net/?p=37
    [menu_order] => 0
    [post_type] => post
    [post_mime_type] =>
    [comment_count] => 0
    [filter] => raw
)
 */